package biz

import (
	"context"
	"errors"
	"fmt"
	"math/rand"
	"time"
)

// SeckillProductRepo 秒杀商品仓储接口，定义秒杀商品相关的数据操作方法
// 包括列表、详情、创建、唯一性校验、库存操作等
type SeckillProductRepo interface {
	List(ctx context.Context, page, pageSize int) ([]*SeckillProduct, int, error)                // 分页查询秒杀商品列表
	Get(ctx context.Context, id int64) (*SeckillProduct, error)                                  // 查询秒杀商品详情
	Exists(ctx context.Context, productId, eventId int64) bool                                   // 校验商品是否已参与活动
	CreateWithStock(ctx context.Context, sp *SeckillProduct, stock int) (*SeckillProduct, error) // 创建秒杀商品并扣减库存
	GetProduct(ctx context.Context, productId int64) (*Product, error)                           // 查询商品详情
	PreheatAllSeckillProducts(ctx context.Context) error                                         // 批量预热即将开始或正在进行的秒杀商品
	ListDetail(ctx context.Context, page, pageSize int) ([]*SeckillProductDetail, int, error)    // 分页查询秒杀商品列表（带商品和活动信息）
}

// SeckillProductUsecase 秒杀商品用例层，封装秒杀商品相关业务逻辑
type SeckillProductUsecase struct {
	repo SeckillProductRepo
}

// NewSeckillProductUsecase 创建秒杀商品用例实例
func NewSeckillProductUsecase(repo SeckillProductRepo) *SeckillProductUsecase {
	return &SeckillProductUsecase{repo: repo}
}

// List 分页查询秒杀商品列表
func (uc *SeckillProductUsecase) List(ctx context.Context, page, pageSize int) ([]*SeckillProduct, int, error) {
	if page <= 0 {
		page = 1
	}
	if pageSize <= 0 || pageSize > 100 {
		pageSize = 20
	}
	return uc.repo.List(ctx, page, pageSize)
}

// Get 查询秒杀商品详情
func (uc *SeckillProductUsecase) Get(ctx context.Context, id int64) (*SeckillProduct, error) {
	if id <= 0 {
		return nil, errors.New("秒杀商品ID无效")
	}
	return uc.repo.Get(ctx, id)
}

// Exists 校验商品是否已参与活动
func (uc *SeckillProductUsecase) Exists(ctx context.Context, productId, eventId int64) bool {
	return uc.repo.Exists(ctx, productId, eventId)
}

// CreateWithStock 创建秒杀商品并扣减库存
func (uc *SeckillProductUsecase) CreateWithStock(ctx context.Context, sp *SeckillProduct, stock int) (*SeckillProduct, error) {
	// 数据验证
	if err := uc.validateSeckillProduct(sp); err != nil {
		return nil, err
	}

	// 检查商品是否存在
	product, err := uc.repo.GetProduct(ctx, sp.ProductID)
	if err != nil {
		return nil, errors.New("商品不存在")
	}

	// 检查商品库存是否足够
	if product.Stock < stock {
		return nil, errors.New("商品库存不足")
	}

	// 检查商品是否已参与该活动
	if uc.repo.Exists(ctx, sp.ProductID, sp.SeckillEventID) {
		return nil, errors.New("商品已参与该活动")
	}

	// 检查秒杀时间是否合理
	now := time.Now()
	if sp.SeckillStart.Before(now) {
		return nil, errors.New("秒杀开始时间不能早于当前时间")
	}
	if sp.SeckillEnd.Before(sp.SeckillStart) {
		return nil, errors.New("秒杀结束时间不能早于开始时间")
	}

	return uc.repo.CreateWithStock(ctx, sp, stock)
}

// GetProduct 查询商品详情
func (uc *SeckillProductUsecase) GetProduct(ctx context.Context, productId int64) (*Product, error) {
	return uc.repo.GetProduct(ctx, productId)
}

// validateSeckillProduct 验证秒杀商品数据
func (uc *SeckillProductUsecase) validateSeckillProduct(sp *SeckillProduct) error {
	if sp.ProductID <= 0 {
		return errors.New("商品ID无效")
	}
	if sp.SeckillPrice <= 0 {
		return errors.New("秒杀价格必须大于0")
	}
	if sp.SeckillStock <= 0 {
		return errors.New("秒杀库存必须大于0")
	}
	if sp.SeckillEventID <= 0 {
		return errors.New("秒杀活动ID无效")
	}
	return nil
}

// SeckillEventRepo 秒杀活动仓储接口，定义活动相关的数据操作方法
// 包括创建、列表、详情、唯一性校验、时间冲突校验等
type SeckillEventRepo interface {
	Create(ctx context.Context, e *SeckillEvent) (*SeckillEvent, error)         // 创建活动
	List(ctx context.Context, page, pageSize int) ([]*SeckillEvent, int, error) // 分页查询活动列表
	Get(ctx context.Context, id int64) (*SeckillEvent, error)                   // 查询活动详情
	NameExists(ctx context.Context, name string) bool                           // 校验活动名唯一性
	TimeConflict(ctx context.Context, start, end time.Time) bool                // 校验活动时间冲突
}

type SeckillEventUsecase struct {
	repo SeckillEventRepo
}

// NewSeckillEventUsecase 创建秒杀活动用例实例
func NewSeckillEventUsecase(repo SeckillEventRepo) *SeckillEventUsecase {
	return &SeckillEventUsecase{repo: repo}
}

// Create 创建活动
func (uc *SeckillEventUsecase) Create(ctx context.Context, e *SeckillEvent) (*SeckillEvent, error) {
	// 数据验证
	if err := uc.validateSeckillEvent(e); err != nil {
		return nil, err
	}

	// 检查活动名唯一性
	if uc.repo.NameExists(ctx, e.Name) {
		return nil, errors.New("活动名称已存在")
	}

	// 检查时间冲突
	if uc.repo.TimeConflict(ctx, e.StartTime, e.EndTime) {
		return nil, errors.New("活动时间与其他活动冲突")
	}

	return uc.repo.Create(ctx, e)
}

// List 分页查询活动列表
func (uc *SeckillEventUsecase) List(ctx context.Context, page, pageSize int) ([]*SeckillEvent, int, error) {
	if page <= 0 {
		page = 1
	}
	if pageSize <= 0 || pageSize > 100 {
		pageSize = 20
	}
	return uc.repo.List(ctx, page, pageSize)
}

// Get 查询活动详情
func (uc *SeckillEventUsecase) Get(ctx context.Context, id int64) (*SeckillEvent, error) {
	if id <= 0 {
		return nil, errors.New("活动ID无效")
	}
	return uc.repo.Get(ctx, id)
}

// validateSeckillEvent 验证秒杀活动数据
func (uc *SeckillEventUsecase) validateSeckillEvent(e *SeckillEvent) error {
	if e.Name == "" {
		return errors.New("活动名称不能为空")
	}
	if len(e.Name) > 100 {
		return errors.New("活动名称长度不能超过100个字符")
	}
	if e.StartTime.Before(time.Now()) {
		return errors.New("活动开始时间不能早于当前时间")
	}
	if e.EndTime.Before(e.StartTime) {
		return errors.New("活动结束时间不能早于开始时间")
	}
	return nil
}

// NameExists 校验活动名唯一性
func (uc *SeckillEventUsecase) NameExists(ctx context.Context, name string) bool {
	return uc.repo.NameExists(ctx, name)
}

// TimeConflict 校验活动时间冲突
func (uc *SeckillEventUsecase) TimeConflict(ctx context.Context, start, end time.Time) bool {
	return uc.repo.TimeConflict(ctx, start, end)
}

// SeckillOrderRepo 秒杀订单仓储接口，定义订单相关的数据操作方法
// 包括创建、查询、列表等
// 清理多余方法，只保留基础方法
type SeckillOrderRepo interface {
	Create(ctx context.Context, o *SeckillOrder) (*SeckillOrder, error)                       // 创建订单
	Get(ctx context.Context, id int64) (*SeckillOrder, error)                                 // 查询订单详情
	GetByOrderNo(ctx context.Context, orderNo string) (*SeckillOrder, error)                  // 新增：根据订单编号查询订单
	List(ctx context.Context, userId int64, page, pageSize int) ([]*SeckillOrder, int, error) // 分页查询订单列表
	GetAlipayService() interface {
		GeneratePayURL(ctx context.Context, orderId string, subject string, amount float64) (string, error)
	} // 获取支付宝服务
}

// SeckillOrderUsecase 秒杀订单用例层，封装订单相关业务逻辑
type SeckillOrderUsecase struct {
	repo SeckillOrderRepo
	spuc *SeckillProductUsecase // 秒杀商品用例层依赖
}

// NewSeckillOrderUsecase 创建订单用例实例
func NewSeckillOrderUsecase(repo SeckillOrderRepo, spuc *SeckillProductUsecase) *SeckillOrderUsecase {
	return &SeckillOrderUsecase{repo: repo, spuc: spuc}
}

// Create 创建订单，返回订单ID和支付链接
func (uc *SeckillOrderUsecase) Create(ctx context.Context, o *SeckillOrder) (*SeckillOrder, string, error) {
	// 校验用户ID
	if o.UserID <= 0 {
		return nil, "", errors.New("用户ID无效")
	}
	// 校验秒杀商品ID
	if o.SeckillProductID <= 0 {
		return nil, "", errors.New("秒杀商品ID无效")
	}
	// 校验数量
	if o.Quantity <= 0 {
		return nil, "", errors.New("购买数量必须大于0")
	}
	// 校验是否已下过单（防止重复秒杀）
	existingOrders, _, _ := uc.repo.List(ctx, o.UserID, 1, 1)
	for _, order := range existingOrders {
		if order.SeckillProductID == o.SeckillProductID && order.Status != "已取消" {
			return nil, "", errors.New("您已参与过本商品秒杀")
		}
	}
	// 获取秒杀商品信息
	sp, err := uc.spuc.Get(ctx, o.SeckillProductID)
	if err != nil {
		return nil, "", errors.New("秒杀商品不存在")
	}
	// 校验库存
	if sp.SeckillStock < o.Quantity {
		return nil, "", errors.New("库存不足")
	}
	// 自动填充商品ID和价格
	o.ProductID = sp.ProductID
	o.SeckillPrice = sp.SeckillPrice
	// 生成唯一订单号
	o.OrderNo = generateOrderNo()
	// 计算订单总价
	o.TotalPrice = sp.SeckillPrice * float64(o.Quantity)
	// 订单状态初始化
	if o.Status == "" {
		o.Status = "待支付"
	}
	order, err := uc.repo.Create(ctx, o)
	if err != nil {
		return nil, "", err
	}
	// 生成支付宝支付链接
	payUrl := ""
	if repoWithAlipay, ok := uc.repo.(interface {
		GetAlipayService() interface {
			GeneratePayURL(ctx context.Context, orderId string, subject string, amount float64) (string, error)
		}
	}); ok {
		payUrl, _ = repoWithAlipay.GetAlipayService().GeneratePayURL(ctx,
			order.OrderNo, // 修正：传递订单编号
			"秒杀订单支付",
			order.TotalPrice,
		)
	}
	return order, payUrl, nil
}

// generateOrderNo 生成唯一订单编号
func generateOrderNo() string {
	t := time.Now()
	rand.Seed(t.UnixNano())
	return fmt.Sprintf("ODR%v%06d", t.Format("20060102150405"), rand.Intn(1000000))
}

// Get 查询订单详情
func (uc *SeckillOrderUsecase) Get(ctx context.Context, id int64) (*SeckillOrder, error) {
	return uc.repo.Get(ctx, id)
}

// List 分页查询订单列表
func (uc *SeckillOrderUsecase) List(ctx context.Context, userId int64, page, pageSize int) ([]*SeckillOrder, int, error) {
	return uc.repo.List(ctx, userId, page, pageSize)
}

// GetByOrderNo 根据订单编号查询订单
func (uc *SeckillOrderUsecase) GetByOrderNo(ctx context.Context, orderNo string) (*SeckillOrder, error) {
	return uc.repo.GetByOrderNo(ctx, orderNo)
}

// GetAlipayService 获取支付宝服务
func (uc *SeckillOrderUsecase) GetAlipayService() interface {
	GeneratePayURL(ctx context.Context, orderId string, subject string, amount float64) (string, error)
} {
	return uc.repo.GetAlipayService()
}

// SeckillProductDetail 秒杀商品详情扩展结构体，包含商品、活动、状态等信息
// 与 data 层结构保持一致
// 建议统一以 biz 层为主
type SeckillProductDetail struct {
	SeckillProduct
	ProductName  string
	ProductImage string
	OriginPrice  float64
	EventName    string
	Status       string
}

// GetDetail 查询秒杀商品详情（带商品和活动信息）
func (uc *SeckillProductUsecase) GetDetail(ctx context.Context, id int64) (*SeckillProductDetail, error) {
	if repo, ok := uc.repo.(interface {
		GetDetail(context.Context, int64) (*SeckillProductDetail, error)
	}); ok {
		return repo.GetDetail(ctx, id)
	}
	return nil, errors.New("repo does not implement GetDetail")
}

// ListDetail 分页查询秒杀商品列表（带商品和活动信息）
func (uc *SeckillProductUsecase) ListDetail(ctx context.Context, page, pageSize int) ([]*SeckillProductDetail, int, error) {
	if repo, ok := uc.repo.(interface {
		ListDetail(context.Context, int, int) ([]*SeckillProductDetail, int, error)
	}); ok {
		return repo.ListDetail(ctx, page, pageSize)
	}
	return nil, 0, errors.New("repo does not implement ListDetail")
}
